package com.ec.system.modules.sys.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ec.common.constant.CommonConstant;
import com.ec.core.common.constants.CoreCommonConstant;
import com.ec.core.common.constants.TenantConstant;
import com.ec.core.common.core.node.NodeManager;
import com.ec.core.common.core.secure.TokenUserInfo;
import com.ec.core.common.exception.BizException;
import com.ec.core.common.utils.BeanUtilEx;
import com.ec.core.common.utils.CoreToolUtil;
import com.ec.core.common.utils.DateUtilEx;
import com.ec.core.common.utils.StringUtilsEx;
import com.ec.core.mybatis.entity.BaseEntity;
import com.ec.core.mybatis.entity.TenantEntity;
import com.ec.core.redis.utils.CoreRedisUtil;
import com.ec.core.secure.utils.SecureUtil;
import com.ec.system.common.enums.RespResultEnum;
import com.ec.system.modules.sys.dto.QueryMenuDTO;
import com.ec.system.modules.sys.dto.SysMenuDTO;
import com.ec.system.modules.sys.entity.SysMenu;
import com.ec.system.modules.sys.entity.SysRole;
import com.ec.system.modules.sys.entity.SysTopMenu;
import com.ec.system.modules.sys.mapper.SysMenuMapper;
import com.ec.system.modules.sys.service.ISysMenuService;
import com.ec.system.modules.sys.service.ISysRoleService;
import com.ec.system.modules.sys.service.ISysTopMenuService;
import com.ec.system.modules.sys.vo.SysMenuVO;
import com.ec.system.modules.sys.vo.SysRouteVO;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Eric Lee
 * @Description:
 * @time: 2021/8/7 20:53
 */
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements ISysMenuService {
    @Resource
    private ISysRoleService sysRoleService;
    @Resource
    private ISysTopMenuService sysTopMenuService;

    @Override
    public SysRouteVO leftMenu(Long topMenuId) {
        List<SysMenu> menus;
        List<String> buttons = Lists.newArrayList();
        SysRouteVO sysRouteVO = new SysRouteVO();
        if (topMenuId == 0) {
            menus = getHomePageMenuRoutes();
            // 点击首页会缓存权限信息
            buttons = queryRoleButtons();
        } else {
            // 根据顶部菜单ID获取对应的菜单显示
            menus = queryMenuByTopMenuId(topMenuId);
        }
        sysRouteVO.setMenus(NodeManager.buildTreeNode(convertVO(menus), 0L));
        sysRouteVO.setButtons(buttons);
        return sysRouteVO;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean addMenu(SysMenuDTO sysMenuDTO) {
        return this.save(BeanUtilEx.copyProperties(sysMenuDTO, SysMenu.class));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateMenu(SysMenuDTO sysMenuDTO) {
        SysMenu sysMenu = BeanUtilEx.copyProperties(sysMenuDTO, SysMenu.class);
        LambdaUpdateWrapper<SysMenu> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(SysMenu::getMenuId,sysMenuDTO.getMenuId());
        return this.update(sysMenu, wrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean removeMenu(SysMenuDTO sysMenuDTO) {
        LambdaQueryWrapper<SysMenu> query = new LambdaQueryWrapper<>();
        query.eq(SysMenu::getParentId,sysMenuDTO.getMenuId())
                .eq(BaseEntity::getIsDelete, CommonConstant.NOT_DELETED);
        long count = baseMapper.selectCount(query);
        if (count > 0) {
            throw new BizException(RespResultEnum.THERE_ARE_CHILD_NODES_ERROR);
        }
        return super.removeById(sysMenuDTO.getMenuId());
    }

    @Override
    public boolean removeMenuFromDb(Long menuId) {
        LambdaQueryWrapper<SysMenu> query = new LambdaQueryWrapper<>();
        query.eq(SysMenu::getParentId,menuId);
        if (count(query) > 0) {
            throw new BizException(RespResultEnum.THERE_ARE_CHILD_NODES_ERROR);
        }
        return baseMapper.deleteByMenuIdFromDb(menuId) > 0;
    }

    @Override
    public List<SysMenuVO> listMenu(QueryMenuDTO queryMenuDTO) {
        List<SysMenu> menus = allSysMenu(Collections.singletonList(queryMenuDTO.getType()));
        return NodeManager.buildTreeNode(convertVO(menus), 0L);
    }

    @Override
    public List<SysMenuVO> userOwnedMenus() {
        TokenUserInfo user = SecureUtil.getUser();
        List<SysMenu> menus;
        List<Integer> types = Arrays.asList(CommonConstant.TYPE_MENU, CommonConstant.TYPE_HYPERLINK);
        if (SecureUtil.isSysSuperAdmin()) {
            menus = allSysMenu(types);
        } else {
            menus = baseMapper.queryRoleMenusOrButtonsByTypes(user.getRoleIds(), types);
        }
        return NodeManager.buildTreeNode(convertVO(menus), 0L);
    }

    @Override
    public List<SysMenuVO> tenantOwnedMenus() {
        List<SysMenu> menus;
        if (SecureUtil.isHeadquarters()) {
            menus = allSysMenu(null);
        } else {
            menus = getTenantMenuAndButton(SecureUtil.userTenantId());
        }
        return NodeManager.buildTreeNode(convertVO(menus), 0L);
    }

    private List<SysMenu> getTenantMenuAndButton(Long tenantId) {
        LambdaQueryWrapper<SysRole> query = new LambdaQueryWrapper<>();
        query.select(SysRole::getRoleId)
                .eq(TenantEntity::getTenantId,tenantId)
                .eq(SysRole::getRoleCode,TenantConstant.TENANT_ADMINISTRATOR);
        List<SysMenu> menus = Lists.newArrayList();
        SysRole sysRole = sysRoleService.getBaseMapper().selectOne(query);
        if (!Objects.isNull(sysRole)) {
            menus = baseMapper.queryRoleMenusOrButtons(Collections.singletonList(sysRole.getRoleId()), null);
        }
        return menus;
    }


    @Override
    public List<SysMenuVO> selectMenuByTenantId(Long tenantId) {
        List<SysMenu> menus = getTenantMenuAndButton(tenantId);
        return convertVO(menus);
    }

    @Override
    public List<SysMenuVO> getAllHyperlinkMenu() {
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysMenu::getType, CommonConstant.TYPE_HYPERLINK)
                .eq(SysMenu::getParentId,0)
                .orderByDesc(SysMenu::getSort);
        List<SysMenu> sysMenus = baseMapper.selectList(wrapper);
        return BeanUtilEx.copyToList(sysMenus,SysMenuVO.class);
    }

    private List<SysMenuVO> convertVO(List<SysMenu> entities) {
        List<SysMenuVO> list = Lists.newArrayList();
        if (!CollectionUtil.isEmpty(entities)) {
            list = entities.stream().map(e -> {
                SysMenuVO sysMenuVO = new SysMenuVO();
                BeanUtilEx.copyProperties(e, sysMenuVO);
                sysMenuVO.setId(e.getMenuId());
                return sysMenuVO;
            }).collect(Collectors.toList());
        }
        return list;
    }

    private List<String> queryRoleButtons() {
        TokenUserInfo user = SecureUtil.getUser();
        List<String> buttons = Lists.newArrayList();
        List<SysMenu> sysMenus = baseMapper.queryRoleMenusOrButtonsByTypes(user.getRoleIds(),
                Collections.singletonList(CommonConstant.TYPE_BUTTON));
        if (!CollectionUtil.isEmpty(sysMenus)) {
            Map<String, Object> cacheMap = Maps.newHashMap();
            buttons = sysMenus.stream().map(SysMenu::getCode).collect(Collectors.toList());
            String strButton = StringUtilsEx.join(buttons, ",");
            cacheMap.put(CoreCommonConstant.PERMISSION_KEY, strButton);
            long tokenExpire = user.getTokenExpire();
            long curTime = DateUtilEx.currentTimeStamp(DateUtilEx.TIME_STAMP_S);
            CoreRedisUtil.hmset(CoreCommonConstant.CACHE_LOGIN_USER_INFO_KEY + user.getUserId(), cacheMap, (tokenExpire - curTime));
        }
        return buttons;
    }

    public List<SysMenu> queryMenuByTopMenuId(Long topMenuId) {
        List<SysMenu> list = Lists.newArrayList();
        SysTopMenu sysTopMenu = sysTopMenuService.getBaseMapper().selectById(topMenuId);
        if (!Objects.isNull(sysTopMenu) && !StringUtils.isBlank(sysTopMenu.getRelationMenuIds())) {
            List<Long> idList = Arrays.stream(sysTopMenu.getRelationMenuIds().split(",")).mapToLong(Long::parseLong).boxed().collect(Collectors.toList());
            List<Long> topMenuIds = idList;
            if (!SecureUtil.isSysSuperAdmin()) {
                // 获取当前用户所属角色拥有的实际权限，排除掉权限之外顶部对应的菜单
                List<Integer> menuTypes = Arrays.asList(CommonConstant.TYPE_MENU, CommonConstant.TYPE_HYPERLINK);
                List<SysMenu> roleMenus = baseMapper.queryRoleMenusOrButtonsByTypes(SecureUtil.getUser().getRoleIds(), menuTypes);
                List<Long> roleMenuIds = roleMenus.stream().map(SysMenu::getMenuId).collect(Collectors.toList());
                topMenuIds = idList.stream().filter(roleMenuIds::contains).collect(Collectors.toList());
            }
            if (!CollectionUtil.isEmpty(topMenuIds)) {
                list = baseMapper.selectBatchIds(topMenuIds);
                list.sort(Comparator.comparing(SysMenu::getSort).reversed());
            }
        }
        return list;
    }

    /**
     * 所有系统菜单
     *
     * @Param type 菜单类型 0：菜单 ，1：按钮 ， null:查询菜单和按钮
     */
    private List<SysMenu> allSysMenu(List<Integer> types) {
        LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(BaseEntity::getIsDelete, CommonConstant.NOT_DELETED);
        if (CoreToolUtil.isNotEmpty(types) && CoreToolUtil.isNotEmpty(types.stream().filter(Objects::nonNull).collect(Collectors.toList()))) {
            wrapper.in(SysMenu::getType,types);
        }
        wrapper.orderByDesc(SysMenu::getSort);
        return baseMapper.selectList(wrapper);
    }

    /**
     * 获取首页菜单
     */
    private List<SysMenu> getHomePageMenuRoutes() {
        List<SysMenu> menus;
        List<Integer> menuTypes = Arrays.asList(CommonConstant.TYPE_MENU, CommonConstant.TYPE_HYPERLINK);
        if (SecureUtil.isSysSuperAdmin()) {
            menus = allSysMenu(menuTypes);
        } else {
            TokenUserInfo user = SecureUtil.getUser();
            List<Long> roleIds = user.getRoleIds();
            menus = baseMapper.queryRoleMenusOrButtonsByTypes(roleIds, menuTypes);
        }
        return menus;
    }
}
